package com.sgcc.nfc.lock.core.nfc

import android.nfc.tech.NfcA
import android.util.Log
import java.io.IOException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

class NfcDeviceManager(private val nfcA: NfcA) {

    data class Device(val startAddress: Int) {
        fun shift(offset: Int): Int = startAddress + offset * 4
    }

    data class DeviceInfo(val id: Long, val isNew: Boolean)

    data class ChargeLevel(val level: Int, val complete: Boolean)

    enum class Result { OK, ERROR }

    enum class Control(val value: Int) { LOCK(0x01), UNLOCK(0x02) }

    suspend fun init(): Pair<Result, Device?> = withContext(Dispatchers.IO) {
        try {
            if (!nfcA.isConnected) {
                nfcA.connect()
            }
            nfcA.timeout = DEFAULT_TIMEOUT
            val started = NfcRawHelper.start(nfcA)
            if (!started) {
                return@withContext Result.ERROR to null
            }
            val address = NfcRawHelper.getStartAddress(nfcA) ?: return@withContext Result.ERROR to null
            Result.OK to Device(address)
        } catch (ex: IOException) {
            Log.e(TAG, "init error", ex)
            Result.ERROR to null
        }
    }

    suspend fun getInfo(device: Device): Pair<Result, DeviceInfo?> {
        var id = 0L
        var isNew = false
        val commands = listOf(
            NfcCommandBuilder.id(),
            NfcCommandBuilder.count()
        )
        val ok = transAndCheck(device, commands) { response, index ->
            when (index) {
                0 -> {
                    val high = response.getOrNull(3)?.toLong()?.and(0xFFFFFFFFL) ?: return@transAndCheck false
                    val low = response.getOrNull(4)?.toLong()?.and(0xFFFFFFFFL) ?: return@transAndCheck false
                    id = (high shl 32) or low
                }
                1 -> {
                    val value = response.getOrNull(3) ?: return@transAndCheck false
                    isNew = value.ushr(24) == 0
                }
            }
            true
        }
        return if (ok) {
            Result.OK to DeviceInfo(id, isNew)
        } else {
            Result.ERROR to null
        }
    }

    fun deriveWorkKey(password: ByteArray, info: DeviceInfo): ByteArray {
        require(password.size == 16) { "Password key must be 16 bytes" }
        val seed = ByteArray(16)
        for (i in 0 until 8) {
            val shift = (7 - i) * 8
            val value = ((info.id shr shift) and 0xFF).toInt().toByte()
            seed[i] = value
            seed[i + 8] = value
        }
        return AesUtil.encrypt(seed, password)
    }

    suspend fun getChargeLevel(device: Device, key: ByteArray): Pair<Result, ChargeLevel?> {
        var threshold = 0
        var raw = 0
        val commands = listOf(
            NfcCommandBuilder.chargeThreshold(key),
            NfcCommandBuilder.chargeCurrent(key)
        )
        val ok = transAndCheck(device, commands) { response, index ->
            val decrypted = AesUtil.decryptUInt(response, 1, response.lastIndex, key)
            val field = decrypted.getOrNull(3) ?: return@transAndCheck false
            val value = (field ushr 16) and 0xFFFF
            if (index == 0) {
                threshold = value
                Log.d(TAG, "Charge threshold: $threshold")
            } else {
                raw = value
                Log.d(TAG, "Charge raw value: $raw")
            }
            true
        }
        if (!ok || threshold <= 0) {
            Log.e(TAG, "Failed to get charge level: ok=$ok, threshold=$threshold")
            return Result.ERROR to null
        }
        try {
            val level = if (threshold > 0) (raw * 100 / threshold).coerceIn(0, 100) else 0
            val complete = level >= 100
            Log.d(TAG, "Charge level: $level%, complete: $complete")
            return Result.OK to ChargeLevel(level, complete)
        } catch (e: Exception) {
            Log.e(TAG, "Error calculating charge level", e)
            return Result.ERROR to null
        }
    }

    suspend fun control(device: Device, key: ByteArray, ctrl: Control, username: String): Result {
        val commands = listOf(
            NfcCommandBuilder.dt(key),
            NfcCommandBuilder.usr(key, username),
            NfcCommandBuilder.arm(key, ctrl.value),
            NfcCommandBuilder.ctrl(key, ctrl.value)
        )
        val ok = transAndCheck(device, commands) { response, index ->
            // 检查每个命令的响应状态
            if (response.isEmpty() || response[0] ushr 16 <= 0) {
                Log.e(TAG, "Command ${index + 1} failed: invalid response header")
                return@transAndCheck false
            }
            true
        }
        return if (ok) Result.OK else Result.ERROR
    }

    suspend fun getControlProgress(device: Device, key: ByteArray): Pair<Result, Int?> {
        var progress = 0
        val commands = listOf(NfcCommandBuilder.controlProgress(key))
        val ok = transAndCheck(device, commands) { response, _ ->
            val decrypted = AesUtil.decryptUInt(response, 1, response.lastIndex, key)
            val field = decrypted.getOrNull(3) ?: return@transAndCheck false
            progress = (field ushr 24) and 0xFF
            true
        }
        return if (ok) Result.OK to progress else Result.ERROR to null
    }

    fun close() {
        try {
            if (nfcA.isConnected) {
                nfcA.close()
            }
        } catch (ex: IOException) {
            Log.e(TAG, "close error", ex)
        }
    }

    private suspend fun transAndCheck(
        device: Device,
        commands: List<List<Int>>,
        onResult: (List<Int>, Int) -> Boolean
    ): Boolean {
        commands.forEachIndexed { index, command ->
            val written = NfcRawHelper.write(nfcA, device.startAddress, 0x00, command)
            if (!written) {
                return false
            }
            val response = NfcRawHelper.read(nfcA, device.shift(1), 9) ?: return false
            val header = response.firstOrNull() ?: return false
            if (header ushr 16 <= 0) {
                return false
            }
            if (!onResult(response, index)) {
                return false
            }
        }
        return true
    }

    companion object {
        private const val TAG = "NfcDeviceManager"
        private const val DEFAULT_TIMEOUT = 5000
        const val DEFAULT_USER = "user"
    }
}
